//
//  UWTile.m
//  Urban Words
//
//  Created by Tim Brandin on 2011-12-05.
//  Copyright (c) 2011 Sypreme. All rights reserved.
//

#import "UWTile.h"
#import "BoardPrefs.h"
#import "UWGameBoard.h"
#import "UWGameView.h"

@interface UWPoint : NSObject
@property float x, y;
@end

@implementation UWPoint
@synthesize x, y;
@end

static int letterValues[26] = {1,3,3,2,1,4,2,4,1,8,5,1,3,1,1,3,10,1,1,1,1,4,4,8,4,10};

@interface UWTile()

// private methods & properties

@property (retain, nonatomic) NSTimer *timer;

- (CGPoint)snapping:(CGPoint)point;
- (void)pan:(CGPoint)point;
- (void)panAnimate;
- (void)startPanAnimate;
- (CGPoint)determinePosition:(CGPoint)point;
- (CGPoint)closestFreePosition:(CGPoint)point Near:(CGPoint)pos;

@end


@implementation UWTile

@synthesize superview, scrollview;
@synthesize distance, timer, brick, remove;
@synthesize panelPos, letter, brick_state;

- (id)initWithView:(UIView *)view scrollView:(UIView *)scroll withChar:(NSString *)text
{
    self = [super init];
    if (self) {
        // custom initialization
        [self setBackgroundColor:[UIColor whiteColor]];
        
        // variable definition
        is_dragging = NO;
        lastmove = CGPointMake(0, 0);
        brick = -1;
        remove = NO;
        
        self.superview = view;
        self.scrollview = scroll;
        self.frame = CGRectMake(0, 0, TILE_SIZE, TILE_SIZE);
        [self setExclusiveTouch:YES];
        
        // setup graphics
        [self setBackgroundColor:[UIColor clearColor]];
        
        CGImageRef imageRef = CGImageCreateWithImageInRect([UIImage imageNamed:@"tile"].CGImage, 
                                                           CGRectMake(0, 0, DISPLAY_TILE_SIZE, DISPLAY_TILE_SIZE));
        highlighted = [[UIImage imageWithCGImage:imageRef] retain];
        
        CGImageRelease(imageRef);
        
        imageRef = CGImageCreateWithImageInRect([UIImage imageNamed:@"tile"].CGImage, 
                                                           CGRectMake(0, DISPLAY_TILE_SIZE + 2, DISPLAY_TILE_SIZE, DISPLAY_TILE_SIZE));
        normal = [[UIImage imageWithCGImage:imageRef] retain];
        
        [self setImage:highlighted forState:UIControlStateNormal];
        [self setImage:highlighted forState:UIControlStateHighlighted];
        
        letter = [[UILabel alloc] initWithFrame:self.frame];
        letter.text = text;
        letter.backgroundColor = [UIColor clearColor];
        letter.font = [UIFont fontWithName:@"Helvetica" size:34];
        letter.textAlignment = UITextAlignmentCenter;
        letter.shadowColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.75];
        letter.shadowOffset = CGSizeMake(-0.5, -0.5);
        
        whiteletter =  [[UILabel alloc] initWithFrame:self.frame];
        whiteletter.text = text;
        whiteletter.backgroundColor = [UIColor clearColor];
        whiteletter.textColor = [UIColor clearColor];
        whiteletter.textAlignment = UITextAlignmentCenter;
        whiteletter.font = [UIFont fontWithName:@"Helvetica" size:34];
        whiteletter.shadowColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.75];
        whiteletter.shadowOffset = CGSizeMake(0.75, 0.75);
        
        valueletter = [[UILabel alloc] initWithFrame:CGRectMake(0, TILE_TEXT_Y_OFF, TILE_SIZE-7, 18)];
        valueletter.text = [NSString stringWithFormat:@"%i", letterValues[((int)[letter.text characterAtIndex:0]-65)]];
        valueletter.backgroundColor = [UIColor clearColor];
        valueletter.font = [UIFont fontWithName:@"Verdana" size:11];
        valueletter.textAlignment = UITextAlignmentRight;
        valueletter.shadowColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.75];
        valueletter.shadowOffset = CGSizeMake(-0.5, -0.5);
        
        valuewhiteletter =  [[UILabel alloc] initWithFrame:CGRectMake(0, TILE_TEXT_Y_OFF, TILE_SIZE-7, 18)];
        valuewhiteletter.text = valueletter.text;
        valuewhiteletter.backgroundColor = [UIColor clearColor];
        valuewhiteletter.textColor = [UIColor clearColor];
        valuewhiteletter.textAlignment = UITextAlignmentRight;
        valuewhiteletter.font = valueletter.font;
        valuewhiteletter.shadowColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.75];
        valuewhiteletter.shadowOffset = CGSizeMake(0.75, 0.75);
        
        [self addSubview:whiteletter];
        [self addSubview:letter];
        
        [self addSubview:valuewhiteletter];
        [self addSubview:valueletter];
    }
    return self;
}

- (void)highlight
{
    [self setImage:highlighted forState:UIControlStateNormal];
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    is_dragging = YES;
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startPanAnimate) object:nil];
    lastmove = CGPointMake(BOARD_SIZE/2, BOARD_SIZE/2);
    [timer invalidate];
    
    // change state on old brick
    if (self.brick != -1) {
        UWBrick *oldBrick = [((UWGameBoard *)scrollview) getBrickAt:self.brick];
        oldBrick.state = NONE;
        
        self.brick = -1;
    }
    
    if ([scrollview.subviews containsObject:self]) {
        [superview addSubview:self];
    }
    
    UITouch *touch = [touches anyObject];
    CGPoint pos = [touch locationInView:superview];
    
    pos.y -= PREFFERED_TILE_OFF; // adjust so user always sees the tile, under their finger
    
    self.center = pos;
    letter.frame = CGRectMake(0, 0, TILE_SIZE, TILE_SIZE);
    whiteletter.frame = letter.frame;
    
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.05];
    
    // animate to position and scale
    
    float size = TILE_SIZE * PREFFERED_SCALE;
    self.frame = CGRectMake(pos.x - size/2.0, pos.y - size/2.0, size, size);
    self.center = pos;
    
    letter.frame = CGRectMake(0, 0, size, size);
    whiteletter.frame = letter.frame;
    
    valueletter.frame = CGRectMake(-3, TILE_TEXT_Y_OFF, size-13, 30);
    valuewhiteletter.frame = valueletter.frame;
    
    [UIView commitAnimations];
    
    NSString *letterValue = [NSString stringWithFormat:@"%i", letterValues[((int)[letter.text characterAtIndex:0]-65)]];
    
    valueletter.text = letterValue;
    valuewhiteletter.text = letterValue;
    valueletter.font = [UIFont fontWithName:@"Verdana" size:11];
    valuewhiteletter.font = valueletter.font;
    
    [super touchesBegan:touches withEvent:event];
}

- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [super touchesCancelled:touches withEvent:event];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    is_dragging = NO;
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startPanAnimate) object:nil];
    lastmove = CGPointMake(BOARD_SIZE/2, BOARD_SIZE/2);
    [timer invalidate];
    
    UITouch *touch = [touches anyObject];
    CGPoint pos = [touch locationInView:scrollview];
    
    CGPoint pp = [scrollview convertPoint:pos toView:superview];
    
    float scale = 1.0;
    
    if ([superview pointInside:pp withEvent:event]) { // if above panel, drop on panel
        pos = [touch locationInView:superview];
        [self setImage:highlighted forState:UIControlStateNormal];
        [superview addSubview:self];
    } else { // if above scrollview, drop on scrollview
        [scrollview addSubview:self];
        [self setImage:normal forState:UIControlStateNormal];
        scale = (((scrollview.frame.size.width-BOARD_MARGIN*2 +BRICK_MARGIN)/BRICK_COUNT)-BRICK_MARGIN) / BRICK_SIZE;
    }
    
    pos.y -= PREFFERED_TILE_OFF; // adjust so user always sees the tile, under their finger
    
    float size1 = (TILE_SIZE / scale) * PREFFERED_SCALE;
    self.frame = CGRectMake(pos.x - size1/2.0, pos.y - size1/2.0, size1, size1);
    self.center = pos;
    
    // adjust tile position to "nice" drop location
    pos.y += 0;
    
    if ([superview pointInside:pp withEvent:event]) { // if above panel, drop on panel
        pos = CGPointMake(panelPos * 318/7 + 2 + TILE_SIZE/2, superview.bounds.size.height/2 + 3);
    }
    else {
        // determine brick position
        CGPoint gridPos = [self determinePosition:pos];
    
        // find best suited position to drop tile
        CGPoint preferredPos = [self closestFreePosition:gridPos Near:pos];
        
        // snapping to bricks
        pos = [self snapping:pos];
    
        // Brick will get placed here
        self.brick = (preferredPos.x * BRICK_COUNT + preferredPos.y);
        UWBrick *placedOnBrick = [(UWGameBoard *)scrollview getBrickAt:brick];
    
        if (placedOnBrick != nil) {
            
            placedOnBrick.state = PLACED;
            
            switch (placedOnBrick.type) {
                case DL:
                    valueletter.text = [NSString stringWithFormat:@"%i", 2 * letterValues[((int)[letter.text characterAtIndex:0]-65)]];
                    valuewhiteletter.text = valueletter.text;
                    valueletter.font = [UIFont fontWithName:@"Verdana-Bold" size:11];
                    valuewhiteletter.font = valueletter.font;
                    break;
            
                case TL:
                    valueletter.text = [NSString stringWithFormat:@"%i", 3 * letterValues[((int)[letter.text characterAtIndex:0]-65)]];
                    valuewhiteletter.text = valueletter.text;
                    valueletter.font = [UIFont fontWithName:@"Verdana-Bold" size:11];
                    valuewhiteletter.font = valueletter.font;
                    break;
                
                default:
                    break;
            }
        }
    
        pos.x -= (gridPos.x - preferredPos.x) * (BRICK_SIZE + BRICK_MARGIN);
        pos.y -= (gridPos.y - preferredPos.y) * (BRICK_SIZE + BRICK_MARGIN) +1;
        
        // if (preferredPos outside of canvas)
        // place it in panel instead.
        
        if (preferredPos.x < 0 && preferredPos.y < 0) {
            
            [self setImage:highlighted forState:UIControlStateNormal];
            [superview addSubview:self];
            
            self.center = [touch locationInView:superview];
            
            pos = CGPointMake(panelPos * 318/7 + 2 + TILE_SIZE/2, 
                            superview.bounds.size.height/2 + 3);
        }
    }
    
    // determine scale of tile for dropping target
    float size2 = TILE_SIZE;
    float duration = (((size1-size2) - 11.5) / 79.4) * 0.10 + 0.05;
    
    letter.frame = CGRectMake((size1 - size2)/2, (size1 - size2)/2, size2, size2);
    whiteletter.frame = letter.frame;
    
    
    valueletter.frame = CGRectMake((size1 - size2)/2 -3, (size1 - size2)/2 + TILE_TEXT_Y_OFF, TILE_SIZE-7, 30);
    valuewhiteletter.frame = valueletter.frame;
    
    // animate to position and scale
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:duration];
    
    self.frame = CGRectMake(pos.x - size2/2.0, pos.y - size2/2.0, size2, size2);
    self.center = pos;
    
    letter.frame = CGRectMake(0, 0, size2, size2);
    whiteletter.frame = letter.frame;
    
    valueletter.frame = CGRectMake(0, TILE_TEXT_Y_OFF +2, TILE_SIZE-7, 18);
    valuewhiteletter.frame = valueletter.frame;
    
    [UIView commitAnimations];
    
    AudioServicesPlaySystemSound(((UWGameBoard *)scrollview).place);
    
    [super touchesEnded:touches withEvent:event];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    if (is_dragging) {
        if ([touches count] == 1) { // drag with one finger
            UITouch *touch = [touches anyObject];
            
            // coordinates relative to panel
            CGPoint pos = [touch locationInView:superview];
            
            UWPoint *p = [[UWPoint alloc] init];
            p.x = pos.x;
            p.y = pos.y;
            
            float distanceToLastMove = sqrtf(powf(lastmove.x - pos.x, 2) + powf(lastmove.y - pos.y, 2));
            
            if (distanceToLastMove > 7) {
                [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startPanAnimate) object:nil];
            }
            [self performSelector:@selector(startPanAnimate) withObject:nil afterDelay:0.1];
            
            lastmove = pos;
            
            pos.y -= PREFFERED_TILE_OFF;
            
            // sort tiles, when above panel (version 1)
            if ([superview pointInside:pos withEvent:event]) {
                int place = pos.x / (318/7);
                
                [superview bringSubviewToFront:self];
                
                if (panelPos != place) {
                    
                    NSMutableArray *tiles = [((UWGameView *)((UIScrollView *)scrollview.superview).delegate).tiles retain];
                    
                    NSSortDescriptor *sortdescriptor = [[[NSSortDescriptor alloc] 
                                                         initWithKey:@"panelPos" ascending:YES] autorelease];
                    
                    [tiles sortUsingDescriptors:[NSArray arrayWithObject:sortdescriptor]];
                    
                    
                    if (panelPos - place > 1) {
                        /*for (int i=place; i < panelPos; i++) {
                            if (i < [tiles count])
                                ((UWTile *)[tiles objectAtIndex:i]).panelPos++;
                        }*/
                        for (UWTile *t in tiles) {
                            if (t.panelPos >= place 
                                && t.panelPos < panelPos
                                && t != self) {
                                t.panelPos++;
                            } 
                            else if (t.panelPos >= panelPos) {
                                break;
                            }
                        }
                    }
                    else if (panelPos - place < -1) {
                        /*for (int i=panelPos+1; i <= place; i++) {
                            if (i < [tiles count])
                                ((UWTile *)[tiles objectAtIndex:i]).panelPos--;
                        }*/
                        
                        for (UWTile *t in tiles) {
                            if (t.panelPos > panelPos+1
                                && t.panelPos <= place
                                && t != self) {
                                t.panelPos--;
                            } 
                            else if (t.panelPos > place){
                                break;
                            }
                        }
                    }
                    else {
                        for (UWTile *t in tiles) {
                            if (t.panelPos == place
                                && t != self) {
                                t.panelPos = panelPos;
                            } 
                            else if (t.panelPos > place) {
                                break;
                            }
                        }
                            
                    }
                    
                    panelPos = place;
                    
                    
                    [UIView beginAnimations:nil context:NULL];
                    [UIView setAnimationDuration:0.1];
                    
                    for (UWTile *t in tiles) {
                        if ( t != self && t.brick == -1) {
                            t.center = CGPointMake(t.panelPos * 318/7 + 2 + TILE_SIZE/2, superview.bounds.size.height/2 + 3);
                        }
                    }
                        
                    [UIView commitAnimations];
                    
                    [tiles release];
                }
                
            }
            
            /* // TODO: mark preferred brick when hovering above it
            
             */
            
            self.center = pos;
        }
    }
    
    [super touchesMoved:touches withEvent:event];
}

- (void)dealloc
{
    // remove stuff
    
    [super dealloc];
}

#pragma mark Private

// snapping version 3.0
- (CGPoint)snapping:(CGPoint)point
{
    CGPoint offset  = ((UIScrollView *)scrollview.superview).contentOffset;
    CGFloat scale = (((scrollview.frame.size.width-BOARD_MARGIN*2 +BRICK_MARGIN)/BRICK_COUNT)-BRICK_MARGIN) / BRICK_SIZE;
    CGSize size     = scrollview.superview.frame.size;
    
    point.x        -= (float)(((int)point.x - BOARD_MARGIN) % ((int)BRICK_SIZE + BRICK_MARGIN)) - BRICK_SIZE/2;
    point.y        -= (float)(((int)point.y - BOARD_MARGIN) % ((int)BRICK_SIZE + BRICK_MARGIN)) - BRICK_SIZE/2;
    
    float maxX      = offset.x  
                    - (float)((int)(offset.x + BRICK_SIZE/2) 
                                    % (int)(BRICK_SIZE + BRICK_MARGIN)) 
                                    + BRICK_SIZE + BRICK_MARGIN;
    
    float maxY      = offset.y 
                    - (float)((int)(offset.y + BRICK_SIZE/2) 
                                    % (int)(BRICK_SIZE + BRICK_MARGIN)) 
                                    + BRICK_SIZE + BRICK_MARGIN;
    
    float minX      = offset.x 
                    + size.width/scale 
                    - (float)((int)(offset.x + size.height/scale - BRICK_SIZE/2) 
                                    % (int)(BRICK_SIZE + BRICK_MARGIN))
                    + BOARD_MARGIN;
    
    float minY      = offset.y 
                    + size.height/scale 
                    - (float)((int)(offset.y + size.height/scale - BRICK_SIZE/2) 
                                    % (int)(BRICK_SIZE + BRICK_MARGIN))
                    + BOARD_MARGIN;
    
    point.x         = MIN(MAX(point.x, MAX(BOARD_MARGIN + BRICK_SIZE/2, maxX)), MIN(BOARD_SIZE - BRICK_SIZE/2, minX)); 
    point.y         = MIN(MAX(point.y, MAX(BOARD_MARGIN + BRICK_SIZE/2, maxY)), MIN(BOARD_SIZE - BRICK_SIZE/2, minY)); 
    
    return point;
}

- (CGPoint)determinePosition:(CGPoint)point
{
    int x, y;
    x = (point.x - BOARD_MARGIN) / (BRICK_SIZE + BRICK_MARGIN);
    y = (point.y - BOARD_MARGIN) / (BRICK_SIZE + BRICK_MARGIN);
    
    x = MIN(MAX(x, 0), BRICK_COUNT); 
    y = MIN(MAX(y, 0), BRICK_COUNT);
    
    return CGPointMake(x, y);
}

// find closest free position, version 2.0
- (CGPoint)closestFreePosition:(CGPoint)point Near:(CGPoint)pos
{
    int wantedPos = point.x * BRICK_COUNT + point.y;
    
    UWBrick *initBrick = [(UWGameBoard *)scrollview getBrickAt:wantedPos];
    
    // if position under tile isn't free, go into the algorithm
    // to find a better suited brick nearby
    if (initBrick.state == PLACED // something placed
            || [initBrick.letter length] != 0) { // something played
        
        int startX = MAX(point.x-1, 0), // TODO: only allow placing on visible tiles
            startY = MAX(point.y-1, 0),
            mindist = INT_MAX,
            minpos = -1;
        
        for (int i = startX; i <= point.x+1 && i < BRICK_COUNT; i++) {
            // for all tiles around me, see if there is any free
            for (int j = startY; j <= point.y+1 && j < BRICK_COUNT; j++) {
                if (!(i == point.x && j == point.y)) { // avoid checking this tile twice
                    UWBrick *tempBrick = [(UWGameBoard *)scrollview getBrickAt:(i * BRICK_COUNT + j)];
                    
                    if (tempBrick.state == NONE // nothing placed
                        && [tempBrick.letter length] == 0) { // nothing played
                        float dist = sqrtf(
                                               powf(pos.x 
                                                    - i*(BRICK_SIZE + BRICK_MARGIN)
                                                    - BRICK_SIZE/2, 2) 
                                               + powf(pos.y 
                                                    - j*(BRICK_SIZE + BRICK_MARGIN)
                                                    - BRICK_SIZE/2, 2));
                        
                        if (dist < mindist) {
                            mindist = dist;
                            minpos = i * BRICK_COUNT + j;
                        }
                    }
                    
                }
            }
        }
        
        if (minpos<0) { // all tiles around wanted brick is full, move to panel.
            
            return CGPointMake(-1, -1);
        }
        else {
            
            return CGPointMake(floorf(minpos/BRICK_COUNT),
                               minpos % (int)BRICK_COUNT);
        }
        
    }
    
    return CGPointMake(point.x, point.y);
}

- (void)startPanAnimate
{
    if (![timer isValid]) {
        timer = [[NSTimer timerWithTimeInterval:0.02 target:self selector:@selector(panAnimate) userInfo:nil repeats:YES] retain];
    }
    
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}

- (void)panAnimate
{
    if (![timer isValid]) {
        [timer invalidate];
    } else {
        [self pan:lastmove];
    }
}

- (void)pan:(CGPoint)point
{
    // pan the view if near edges
    CGPoint sp = [scrollview.superview convertPoint:point toView:scrollview.superview];
    CGPoint offset = ((UIScrollView *)scrollview.superview).contentOffset;
    BOOL stopPanX = NO, stopPanY = NO;
    if (sp.x < 30 && sp.y < 0) { // scroll left/right
        offset.x -= 5;
    } else if (sp.x > 290 && sp.y < 0) {
        offset.x += 5;
    } else {
        stopPanX = YES;
    }

    if (sp.y > -30 && sp.y < 0) { // scroll up/down
        offset.y += 5;
    } else if (sp.y < -280) {
        offset.y -= 5;
    } else {
        stopPanY = YES;
    }
    
    if (stopPanX && stopPanY) {
        [timer invalidate];
    }
    
    // control stops
    CGSize size = scrollview.superview.frame.size;
    CGFloat scale = ((UIScrollView *)scrollview.superview).contentScaleFactor;
    CGSize contentSize = ((UIScrollView *)scrollview.superview).contentSize;
    
    offset.x = MIN(MAX(offset.x, 0), BOARD_SIZE*scale - size.width);
    offset.y = MIN(MAX(offset.y, 0), BOARD_SIZE*scale - size.height);
    
    if (contentSize.width > size.width) {
        ((UIScrollView *)scrollview.superview).contentOffset = offset;
    } else {
        [timer invalidate];
    }
}

@end
